package cn.chenfeifx.media.controls.skin;

import cn.chenfeifx.core.common.ClipUtil;
import cn.chenfeifx.media.controls.MediaPlayerSlider;
import javafx.beans.binding.Bindings;
import javafx.beans.value.ObservableValue;
import javafx.event.EventHandler;
import javafx.scene.control.SkinBase;
import javafx.scene.effect.DropShadow;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Region;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;

import java.util.concurrent.Callable;
import java.util.function.Consumer;

public class MediaPlayerSliderSkin extends SkinBase<MediaPlayerSlider> {

    /**
     * Constructor for all SkinBase instances.
     *
     * @param control The control for which this Skin should attach to.
     */
    public MediaPlayerSliderSkin(MediaPlayerSlider control) {
        super(control);
        getChildren().add(new SliderRegion());
    }

    public class SliderRegion extends Region {

        private static final double HEIGHT = 4;

        private final Rectangle oneRectangle = new Rectangle(0, HEIGHT);
        private final Rectangle twoRectangle = new Rectangle(0, HEIGHT);
        private final Pane pane = new Pane(oneRectangle, twoRectangle);
        private final Circle circle = new Circle(5);

        public SliderRegion() {
            initialize();
        }

        private void initialize() {
            MediaPlayerSlider skinnable = getSkinnable();
            getChildren().addAll(pane, circle);
            ClipUtil.clipRect(pane, skinnable.arcProperty());
            pane.layoutYProperty().bind(heightProperty().subtract(pane.heightProperty()).divide(2));
            //
            oneRectangle.setFill(Color.rgb(255, 255, 255, 0.2));
            oneRectangle.widthProperty().bind(widthProperty());
            //
            twoRectangle.setFill(Color.web("#409EFF"));
            regAddListener(o -> {
                // 某个值在区间中的百分比 : (当前值 - 区间最小值) / (区间最大值 - 区间最小值)
                double v = (skinnable.getValue() - skinnable.getMin()) / (skinnable.getMax() - skinnable.getMin());
                twoRectangle.setWidth(v * getWidth());
            }, skinnable.valueProperty(), skinnable.minProperty(), skinnable.maxProperty(), widthProperty());
            //
            circle.setFill(Color.WHITE);
            circle.centerXProperty().bind(twoRectangle.widthProperty());
            circle.centerYProperty().bind(heightProperty().divide(2));
            circle.setEffect(new DropShadow(3, Color.rgb(0, 0, 0, 0.5)));
            circle.opacityProperty().bind(Bindings.createObjectBinding((Callable<Number>) ()
                    -> isHover() || isPressed() ? 1 : 0, hoverProperty(), pressedProperty()));
            //
            initDraggedEvent();
        }

        private void setValue() {
            MediaPlayerSlider skinnable = getSkinnable();
            // 某个百分比位置在区间中对应的值 : 区间最小值 + (百分比位置 / 100) * (区间最大值 - 区间最小值)
            double v = skinnable.getMin() + (twoRectangle.getWidth() / getWidth()) * (skinnable.getMax() - skinnable.getMin());
            skinnable.setValue(v);
        }

        @SafeVarargs
        private <T> void regAddListener(Consumer<Object> consumer, ObservableValue<T>... ds) {
            for (ObservableValue<T> observableValue : ds) {
                observableValue.addListener((observable, oldValue, newValue) -> consumer.accept(newValue));
            }
        }

        // 初始化相关事件
        private void initDraggedEvent() {
            MediaPlayerSlider skinnable = getSkinnable();
            EventHandler<? super MouseEvent> eventHandler = event -> {
                event.consume();
                double x = event.getX();
                double width = x < 0 ? 0 : Math.min(x, getWidth());
                twoRectangle.setWidth(width);
                if (MediaPlayerSlider.Mode.ANY.equals(skinnable.getMode())) {
                    setValue();
                }
            };
            // 按下
            setOnMousePressed(eventHandler);
            // 拖拽
            setOnMouseDragged(eventHandler);
            // 释放按钮
            setOnMouseReleased(event -> setValue());
        }

    }
}
